package com.itunic.cloud.configuration;

import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.oauth2.server.resource.introspection.NimbusReactiveOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames;
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.time.Instant;

import static org.springframework.security.oauth2.core.OAuth2AccessToken.TokenType.BEARER;
import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.ISSUED_AT;

/**
 * 缺省令牌交换器。
 * 所有与授权中心交互，均在此完成。
 * Spring 默认会有实现，为了满足业务需求，所以采取自定义。
 *
 * @author Sean
 */
public class UserInfoOpaqueTokenIntrospector implements ReactiveOpaqueTokenIntrospector {
    /**
     * 缺省令牌请求器，
     * 此处可以通过配置文件注入进来。
     * 偷个懒，直接写死。并不影响功能使用。
     */
    private ReactiveOpaqueTokenIntrospector delegate;

    /**
     * 自定义的用户服务
     * 用于构造登录用户的基本信息及权限信息
     */


    private final ReactiveClientRegistrationRepository repository;
    private WebClient.Builder webClient;
    private ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService;

    public UserInfoOpaqueTokenIntrospector(ReactiveClientRegistrationRepository repository) {
        this.repository = repository;
    }

    public UserInfoOpaqueTokenIntrospector(ReactiveClientRegistrationRepository repository, WebClient.Builder webClient) {
        this.repository = repository;
        this.webClient = webClient;
        this.oauth2UserService =
                new CustomReactiveOAuth2UserService(webClient.build());
        this.delegate = new NimbusReactiveOpaqueTokenIntrospector(
                "http://localhost:8080r/introspect",
                webClient.build());
    }

    /**
     * 实现核心的执行器
     *
     * @param token 登录后的token
     * @return
     */
    @Override
    public Mono<OAuth2AuthenticatedPrincipal> introspect(String token) {
        //验证token
        return Mono.zip(this.delegate.introspect(token),
                this.repository.findByRegistrationId("itunic"))
                .map(t -> {
                    //token 验证通过后，构造相关信息
                    OAuth2AuthenticatedPrincipal authorized = t.getT1();
                    ClientRegistration clientRegistration = t.getT2();
                    Instant issuedAt = authorized.getAttribute(ISSUED_AT);
                    Instant expiresAt = authorized
                            .getAttribute(OAuth2IntrospectionClaimNames.EXPIRES_AT);
                    OAuth2AccessToken accessToken =
                            new OAuth2AccessToken(BEARER, token, issuedAt, expiresAt);
                    //构造 user info请求器
                    return new OAuth2UserRequest(clientRegistration, accessToken);
                    //构造用户信息
                }).flatMap(this.oauth2UserService::loadUser);
    }
}